#include <stdio.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <unistd.h>
#include <time.h>

//🀄🈲■█▉

// 宏定义区
#define TTY_PATH  "/dev/tty"
#define STTY_US  "stty raw -echo -F "
#define STTY_DEF  "stty -raw echo -F "
#define WIDTH 15
#define HEIGHT 20

// 全局变量区
int x=5,y=5;
int shapeIndex = 2;	// 数组脚标
int shapeOffset = 0;	// 图形的偏移值
int map[HEIGHT][WIDTH] = {0};	// 坐标系
int moveTime = 30;	// 控制下落速度
int moveTimer = 0;	// 下落计时变量
int score = 0;	// 分数
int level = 1;	// 等级

// 结构体区
//坐标
struct Point{
	int x;
	int y;
};

struct Point shaps[19][4] = {
	{{0,0},{0,-1},{0,-2},{1,0}},//正L1		0
    {{0,0},{0,1},{1,0},{2,0}},//正L2			1
    {{0,0},{-1,0},{0,1},{0,2}},//正L3		2
    {{0,0},{0,-1},{-1,0},{-2,0}},//正L4		3
    {{0,0},{-1,0},{0,-1},{0,-2}},//反L1		4
    {{0,0},{0,-1},{1,0},{2,0}},//反L2		5
    {{0,0},{1,0},{0,1},{0,2}},//反L3			6
    {{0,0},{-1,0},{-2,0},{0,1}},//反L4		7
    {{0,0},{-1,0},{1,0},{0,-1}},//T1		8
    {{0,0},{0,1},{0,-1},{1,0}},//T2			9
    {{0,0},{-1,0},{1,0},{0,1}},//T3			10
    {{0,0},{-1,0},{0,-1},{0,1}},//T4		11
    {{0,0},{-1,0},{1,0},{2,0}},//横条		12
    {{0,0},{0,-1},{0,1},{0,2}},//竖条		13
    {{0,0},{1,0},{0,-1},{-1,-1}},//正Z1		14
    {{0,0},{1,-1},{0,1},{1,0}},//正Z2		15
    {{0,0},{1,-1},{-1,0},{0,-1}},//反Z1		16
    {{0,0},{-1,-1},{-1,0},{0,1}},//反Z2		17
    {{0,0},{-1,-1},{-1,0},{0,-1}}//方块		18
};

// 函数声明区
int getChar(); //非阻塞输入
void drawPrint(int x, int y, int op); // 画一个小方块
void userCtrl(char input);	// 玩家控制输入
void drawShape();	// 绘制俄罗斯方块图形
void transfrom();	// 改变坐标值
void createNewShape();	// 随机产生新图形
void createEdge();	// 赋值边框
void drawMap();	// 绘制边框
int isCanMove(int ind, int x, int y);	// 可移动检测
void moveDown();	// 自动下落
int Rotate(int op);	// 图形旋转函数
int isCanRotate(int op);	// 判断图形是否能够旋转
void addToScree();	// 将图形添加到屏幕数组
int isFull(int line);	// 校验第line行是否满行
void clearLines();	// 循环行数
void moveLine(int src, int desc);	// 消行覆盖函数
void addLevel();	// 升级
int isGameOver();	// 游戏结束

// 函数定义区
int main()
{
	// 种子随机化
	srand(time(0));
	createEdge();
	createNewShape();
    // 屏蔽掉终端的正常输入
	system(STTY_US TTY_PATH);//"stty raw -echo -F ""/dev/tty"
	while(1)
	{
		char c = getChar();// 非阻塞输入
		// 封装程序勿动
        //c-->'a'
        if(isGameOver()){
			printf("\033[%d;%dH",10,5);//将光标移到到10行5列的位置
        	printf("\033[44;31mGame Over!!\033[0m ");//打印gameover
        	fflush(stdout);
        	system(STTY_DEF TTY_PATH);    //恢复非阻塞IO设置
        	printf("\033[%d;%dH",27,0);//将光标移到到27行0列的位置
        	sleep(3);
        	return 0;
		}
		if(c == 3)// 输入ctrl+c 获得到的ascii值是 3
		{
            // 让终端的输入恢复正常
			system(STTY_DEF TTY_PATH);//"stty -raw echo -F ""/dev/tty"
			break;
		}
		
		drawMap();
		userCtrl(c);	// 玩家控制输入
		drawShape();	// 绘制一个俄罗斯方块图形
		fflush(stdout);	//清缓存
		usleep(17 * 1000);//延迟
		moveDown();
		system("clear");//清屏
	}
	return 0;
}

void createEdge(){	// 赋值边框
	for(int i = 0; i < HEIGHT; ++i){
		for(int j = 0; j < WIDTH; ++j){
			if(i == 0 || i == HEIGHT - 1 || j == 0 || j == WIDTH - 1){
				map[i][j] = 2;
			}
		}
	}
}

void drawMap(){	// 绘制Map
	for(int i = 0; i < HEIGHT; ++i){
		for(int j = 0; j < WIDTH; ++j){
			if(map[i][j] == 0){
				continue;
			}
			// 绘制方块 -x -y注意分清这个坐标和数组角标的对应关系
			drawPrint(j, i, map[i][j]);
		}
	}
}

void drawShape(){	// 绘制俄罗斯方块图形
	for(int i = 0; i < 4; ++i){
		drawPrint(x + shaps[shapeIndex][i].x, y + shaps[shapeIndex][i].y, 1);
	}
}

void createNewShape(){	// 随机产生新图形
	// 产生0 - 18的随机数
	shapeIndex = rand() % 19;
	// 产生位置在游戏中间置顶的位置
	x = WIDTH / 2;
	y = 2;
}

//改变shapeIndex的值
//0 -> 1
//1 -> 0
void transfrom(){
	// 改变shapeIndex的值
	// op作为操作数存在
	int op = 0;
	if(shapeIndex >= 0 && shapeIndex <= 11){
		op = 4;
	}else if(shapeIndex >= 12 && shapeIndex <= 17){
		op = 2;
	}else if(shapeIndex == 18){
		return;
	}
	if(isCanRotate(op)){
		shapeIndex = Rotate(op);
	}else{
		return;
	}
}

// 旋转函数
// op -> 操作数
// 返回旋转后的索引
int Rotate(int op){
	int off,ind = shapeIndex;
	off = ind % op;
	ind /= op;
	off++;
	off %= op;
	ind *= op;
	ind += off;
	return ind;
}

// 判断是否可以旋转
// op - 操作数
// 返回0/1
int isCanRotate(int op){
	int ind = Rotate(op);
	if(isCanMove(ind, 0, 0)){
		return 1;
	}else{
		return 0;
	}
}

// 图像添加到屏幕
void addToScree(){
	for(int i = 0; i < 4; ++i){
		map[y + shaps[shapeIndex][i].y][x + shaps[shapeIndex][i].x] = 1;
	}
	// 创建新图形
	createNewShape();
}

//玩家输入控制
//input - 输入字符
void userCtrl(char input){
	switch(input){
		case 'a':
		case 'A':
			if(isCanMove(shapeIndex, -1, 0)){
				x--;
			}
			break;
		case 'd':
		case 'D':
			if(isCanMove(shapeIndex, 1, 0)){
				x++;
			}
			break;
		case 's':
		case 'S':
			if(isCanMove(shapeIndex, 0, 1)){
				y++;
			}
			break;
		case 'w':
		case 'W':
			transfrom();
			break;
		case 'T':
		case 't':
			createNewShape();
			break;
	}
}

// 程序操控，方块自动下落函数
void moveDown(){
	moveTimer++;
	if(isCanMove(shapeIndex, 0, 1) && moveTimer >= moveTime){
		y++;
		moveTimer = 0;
	}else if(!isCanMove(shapeIndex, 0, 1)){
		addToScree();
		clearLines();
	}
}

// 判读line行是否满了
// line - 检测的行号
// 返回0/1
int isFull(int line){
	for(int i = 0; i < WIDTH; ++i){
		if(map[line][i] == 0){
			return 0;
		}
	}
	return 1;
}

// 循环行号判断是否满
void clearLines(){
	int count = 0;
	for(int i = HEIGHT - 2; i > 0; --i){
		if(isFull(i)){
			count++;
		}
		else if(count > 0){
			moveLine(i, i + count);
		}
	}
	score += count * 130;
	addLevel();
}

// 升级
void addLevel(){
	if(score >= level * 420){
		score -= level * 420;
		level++;
	}
}

// 覆盖行号
// src - 从那一行来
// desc - 到哪里去
void moveLine(int src, int desc){
	for(int i = 1; i < WIDTH - 1; i++){
		map[desc][i] = map[src][i];
		map[src][i] = 0;
	}
}

// 判断是否可以移动
// ind - 图形的下标
// x - 在x轴上的偏移值 - 对应数组的[ ][x]
// y - 在y轴上的偏移值 - 对应数组的[y][ ]
int isCanMove(int ind, int xoff, int yoff){
	int xOffset = x + xoff;
	int yOffset = y + yoff;
	for(int i = 0; i < 4; i++){
		if(map[yOffset + shaps[ind][i].y][xOffset + shaps[ind][i].x] != 0){
			return 0;
		}
	}
	return 1;
}

// 游戏结束函数
// 返回0/1
int isGameOver(){
	for(int i = 1; i < WIDTH - 1; ++i){
		if(map[1][i] != 0){
			return 1;
		}
	}
	return 0;
}

// 输入获取函数
// 返回输入字符对应的整形
int getChar()
{
    fd_set rfds;
    struct timeval tv;
    int ch = 0;
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);
    tv.tv_sec = 0;
    tv.tv_usec = 10; 
    if (select(1, &rfds, NULL, NULL, &tv) > 0)
    {
        ch = getchar(); 
    }
    return ch;
}

//在x,y坐标处绘制小正方形
//x - 坐标x
//y - 坐标y
void drawPrint(int x, int y, int op)
{
	printf("\033[%d;%dH", y+1, x*2+1);
	if(op == 1){
		printf("\033[1;37m■ \033[0m");
	}else if(op == 2){
		printf("\033[1;37m🈲 \033[0m");
	}
	printf("\033[%d;%dH",24,0);
    printf("\033[0;34m Level:%d      Score:%d\033[0m ",level,score);
}
